{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 6.3 语言模型数据集（周杰伦专辑歌词）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "1.6.0\ncuda\n"
    }
   ],
   "source": [
    "import torch\n",
    "import random\n",
    "import zipfile\n",
    "\n",
    "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "print(torch.__version__)\n",
    "print(device)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6.3.1 读取数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "'想要有直升机\\n想要和你飞到宇宙去\\n想要和你融化在一起\\n融化在宇宙里\\n我每天每天每'"
     },
     "metadata": {},
     "execution_count": 2
    }
   ],
   "source": [
    "with zipfile.ZipFile('../../data/jaychou_lyrics.txt.zip') as zin:\n",
    "    with zin.open('jaychou_lyrics.txt') as f:\n",
    "        corpus_chars = f.read().decode('utf-8')\n",
    "corpus_chars[:40]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "10000"
     },
     "metadata": {},
     "execution_count": 3
    }
   ],
   "source": [
    "corpus_chars = corpus_chars.replace('\\n', ' ').replace('\\r', ' ')\n",
    "corpus_chars = corpus_chars[0:10000]\n",
    "len(corpus_chars)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6.3.2 建立字符索引"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "{'装', '囱', '残', '啦', '默', '丹', '伊', '再', '咒', '消', '怎', '翰', '味', '啊', '湖', '招', '几', '廓', '惯', '照', '拆', '形', '哭', '处', '逃', '老', '是', '演', '惹', '杂', '香', '幽', '车', '确', '滴', '乡', '岩', '术', '恨', '木', '该', '整', '外', '沙', '缘', '切', '两', '翔', '抬', '苏', '引', '大', '撑', ' ', '雨', '医', '篮', '简', '翻', '嘴', '好', '逅', '南', '恩', '生', '钩', '旧', '办', '谁', '给', '寂', '底', '口', '弓', '春', '盘', '节', '檐', '童', '临', '凉', '反', '控', '任', '悔', '泊', '呜', '七', '下', '飞', '箱', '休', '靠', '熟', '祈', '塞', '碰', '觉', '唱', '息', '密', '啸', '死', '音', '蝠', '嘛', '翅', '印', '坦', '鼻', '受', '爵', '敌', '威', '叫', '丽', '够', '回', '实', '秀', '我', '否', '秒', '斗', '随', '亮', '造', '护', '条', '起', '千', '猎', '峡', '静', '桌', '抄', '绪', '始', '符', '约', '恍', '绕', '骨', '产', '奔', '何', '棒', '弄', '包', '藏', '客', '为', '嘟', '环', '鹿', '夫', '般', '进', '埋', '守', '耍', '才', '功', '景', '成', '欣', '系', '弃', '蒙', '忘', '髅', '争', '睛', '稀', '江', '今', '笔', '胸', '在', '打', '刀', '牌', '血', '狼', '王', '霓', '找', '棍', 'B', '尾', '伯', '烛', '题', '枪', '熬', '漫', '样', '婆', '剩', '九', '元', '者', '肩', '别', '后', '词', '祷', '中', '周', '馆', '窝', '补', '腿', '强', '决', '托', '枝', '蟑', '宁', '银', '迎', '假', '飘', '把', '壁', '脚', '杨', '墟', '虹', '毫', '豆', '乌', '跳', '钢', '长', '白', '手', '铺', '斑', '告', '典', '而', '莹', '蝪', '忙', '梭', '透', '鸠', '事', '融', '令', '北', '情', '就', '抖', '泽', '晴', '相', '注', '毛', '柔', '蛛', '脉', '站', '许', '驳', '没', '睡', '仔', '鲜', '讽', '废', '酒', '荡', '季', '比', '乘', '延', '公', '盯', '歌', '软', '耳', '数', '碗', '蔓', '丛', '蕃', '联', '来', '草', '糗', '闪', '物', '油', '头', '让', '苍', '路', '定', '蜡', '次', '鼠', '秃', '拉', '叹', 'o', '蜘', '哀', '姑', '自', '拥', '制', '皮', '芜', '抽', '投', '转', '度', '操', '象', '水', '主', '怪', '悄', '盒', '仁', '横', '酋', '平', '段', '板', '晰', '征', '虽', '衷', '之', '弥', '型', '鳅', '角', '堂', '队', '踢', '旁', '腐', '解', '伤', '汹', '暖', 'y', '爷', '扫', '西', '田', '潮', '气', '轻', '宠', '左', '菸', '动', '分', '禁', '垂', '庭', '射', '尽', '杵', '门', '正', '凶', 'C', '舍', '道', '拽', '命', '封', '捏', '食', '以', '城', '忠', '远', '怨', '隔', '帅', '慢', '剧', '步', '秋', '野', '场', '被', '殿', '然', '会', '个', '第', '夜', '腮', '瓦', '离', '留', '防', '轮', '通', '烧', '温', '和', '兵', '提', '变', '寞', '于', '清', '苦', '屈', '沟', '课', '爽', '药', 'W', '果', '娇', '懂', '衫', '友', '天', '边', '深', '爸', '机', '还', '早', '涯', '玩', '楷', '吗', '迷', '用', '楔', '直', '泣', '猪', '酱', '遇', '所', '小', '搞', '奈', '占', '或', '妈', '姥', '呆', '海', '短', '兮', '拳', '碎', '梦', '备', '带', '沉', '疲', '硬', '椅', '优', '疑', '又', '却', '值', '穿', '戒', '共', '蜜', '心', '出', '怕', '如', '啃', '换', '信', '泛', '吴', '黑', '得', '昨', '马', '仙', 'u', '法', '你', '恼', '送', '语', '?', '市', '袋', '更', '烦', '养', '阳', '喝', '学', '妥', '非', '同', '琴', '烁', '谷', '枚', '痛', '罩', '呵', '试', '括', '狗', '甘', '运', '属', '激', '想', '它', '完', '除', '邂', '枯', '汉', '揍', '丁', '掉', '朋', '要', '移', '少', '断', '二', '微', '旋', '病', '亏', '止', '抱', '土', '跑', '原', '歉', '葛', '疯', '努', '螂', '纳', '彩', '理', '眼', '窗', '店', '载', '永', '期', '宙', '知', '灵', '金', '模', '悲', '圈', '渡', '戏', '滩', '龙', '危', '百', '惚', '光', '蝙', '福', '距', '都', '蛇', '向', '四', '跟', '害', '写', '细', '前', '布', '甜', '问', '魂', '猜', '颗', '素', '吸', '瑰', '栈', '寄', '橱', '听', '子', '落', '坟', '已', '池', '失', '勉', '去', '也', '骷', '十', '张', '著', '配', '对', '茶', '教', '古', '坊', '到', '蛦', '败', '侬', '鹰', '里', '鸥', '师', '雕', '砖', '明', '认', '邻', '担', '拿', '抢', '吃', '现', '活', '喜', 'J', '年', '指', '缝', '躲', '猫', '榜', '重', '单', '了', '饿', '墙', '球', '管', '红', '险', '待', '亚', '些', '泪', '忧', '河', '言', '见', '间', '呢', '莫', '朵', '摩', '倦', '晶', '经', '烟', '记', '初', 'A', '铜', '无', '膀', '蝴', '喘', '上', '色', '名', '社', '游', '悬', '快', '迹', '倒', '乐', '住', '连', '胖', '耿', '从', '习', '力', '安', 'h', '限', '胡', '宣', '很', '举', '身', '雅', '常', '武', '录', '晚', '层', '升', '哪', '荒', '能', '狠', '屉', '一', '久', '玫', '内', '陪', '纪', '拖', '辈', '泥', 'a', '们', '掩', '义', '时', '哼', '图', '屋', '谢', '满', '村', '巴', '淡', '过', '加', '甩', '散', '当', '面', '干', '巫', '哈', '爱', '种', '字', '朽', '瞎', '难', '忆', '看', '狂', '诅', '吐', '感', '什', '其', '刻', '三', '濡', '停', '呼', '疗', '箭', '专', '像', '盲', '骑', '肉', '承', '蓝', '蝶', '母', '妙', '阻', '世', '欢', '乖', '铁', '杰', '地', '达', '近', '发', '作', '句', '牵', '凯', '奏', '隐', '传', '涌', '拜', '背', '真', '索', '漠', '编', '袭', '脸', '碑', '星', '黄', '篇', '及', '伦', '格', '极', '闷', '仪', '敢', '疼', '鸦', '凝', '界', '缸', '陷', '司', '彿', 'b', '币', '线', '抛', '液', '榉', '欠', '己', '只', '放', '山', '响', '祭', '儿', '攻', '的', '娘', '考', '神', '书', '闭', '全', '卜', '根', '器', '牧', '碌', '怀', '阵', '译', '证', '鸣', '这', '他', '腔', '准', '画', '岁', '馨', '珍', '意', '掌', '灌', '说', '辛', '粥', '念', '钟', '热', '彷', '那', '斯', '沼', '截', '等', '有', '嵩', '躺', '悉', '依', '做', '蜥', '斜', '吧', '坏', '赏', '空', '广', '丘', '寒', '右', '爬', '卷', '行', '育', '东', '容', '醉', '折', '督', '裂', '偷', '领', '脑', '墓', '寻', '美', '午', '日', '点', '刺', '诗', '居', '降', '火', '颁', '开', '么', '藤', '愁', '往', '化', '瓜', '片', '米', '状', '撒', '入', '溪', '女', '奇', '酸', '植', '怒', '漂', '她', '铅', '练', '据', '愿', '半', '走', '排', '每', '故', '风', '街', '不', '因', '多', '彻', '堡', '夕', '斤', '着', '刚', '壶', '视', '萨', '候', '可', '爹', '酿', '云', '家', '桑', '选', '话', '国', '声', '人', '笑', '最', '映', '使', '柳', '代', '逗', '擅', '区', '瘦', '霜', '玄', '方', '花', '羞', '染', '思', '双', '怯', '御', '太', '将', '望', '誓', '诉', '昏', '战', '文', '若', '纯', '廉', '便', '暴', '错', '缓', '林', '纵', '瓣', '宇', '吹', '替', '牛', '性', '必', '镇', '灰', '较', '但', '琳', '目'}\n"
    }
   ],
   "source": [
    "a = set(corpus_chars)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "1027"
     },
     "metadata": {},
     "execution_count": 5
    }
   ],
   "source": [
    "idx_to_char = list(a)\n",
    "len(idx_to_char)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "i: 0 char: 装\ni: 1 char: 囱\ni: 2 char: 残\ni: 3 char: 啦\ni: 4 char: 默\ni: 5 char: 丹\ni: 6 char: 伊\ni: 7 char: 再\ni: 8 char: 咒\ni: 9 char: 消\n"
    }
   ],
   "source": [
    "for i, char in enumerate(idx_to_char[:10]):\n",
    "    print('i:',i,'char:',char)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "{'装': 0, '囱': 1, '残': 2, '啦': 3, '默': 4, '丹': 5, '伊': 6, '再': 7, '咒': 8, '消': 9, '怎': 10, '翰': 11, '味': 12, '啊': 13, '湖': 14, '招': 15, '几': 16, '廓': 17, '惯': 18, '照': 19}\n"
    }
   ],
   "source": [
    "char_to_idx2 = dict([(char, i) for i, char in enumerate(idx_to_char[:20])])\n",
    "print(char_to_idx2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "1027"
     },
     "metadata": {},
     "execution_count": 8
    }
   ],
   "source": [
    "\n",
    "char_to_idx = dict([(char, i) for i, char in enumerate(idx_to_char)])\n",
    "vocab_size = len(char_to_idx)\n",
    "vocab_size"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "[523, 534, 881, 442, 713, 432, 53, 523, 534, 409, 493, 89, 621, 1015, 562, 609, 53, 523, 534, 409]\n"
    }
   ],
   "source": [
    "corpus_indices = [char_to_idx[char] for char in corpus_chars]\n",
    "print(corpus_indices[:20])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "['想', '要', '有', '直', '升', '机', ' ', '想', '要', '和', '你', '飞', '到', '宇', '宙', '去', ' ', '想', '要', '和']\n"
    }
   ],
   "source": [
    "sample = corpus_indices[:20]\n",
    "b = [idx_to_char[idx] for idx in sample]\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "chars: 想要有直升机 想要和你飞到宇宙去 想要和\nindices: [523, 534, 881, 442, 713, 432, 53, 523, 534, 409, 493, 89, 621, 1015, 562, 609, 53, 523, 534, 409]\n"
    }
   ],
   "source": [
    "\n",
    "print('chars:', ''.join(b))\n",
    "print('indices:', sample)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6.3.3 时序数据的采样\n",
    "### 6.3.3.1 随机采样"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 本函数已保存在d2lzh_pytorch包中方便以后使用\n",
    "# batch_size指每个小批量的样本数，\n",
    "# num_steps为每个样本所包含的时间步数\n",
    "def data_iter_random(corpus_indices, batch_size, num_steps, device=None):\n",
    "    # 减1是因为输出的索引x是相应输入的索引y加1\n",
    "    num_examples = (len(corpus_indices) - 1) // num_steps\n",
    "    epoch_size = num_examples // batch_size\n",
    "    example_indices = list(range(num_examples))\n",
    "    random.shuffle(example_indices)\n",
    "\n",
    "    # 返回从pos开始的长为num_steps的序列\n",
    "    def _data(pos):\n",
    "        return corpus_indices[pos: pos + num_steps]\n",
    "    if device is None:\n",
    "        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "    \n",
    "    for i in range(epoch_size):\n",
    "        # 每次读取batch_size个随机样本\n",
    "        i = i * batch_size\n",
    "        batch_indices = example_indices[i: i + batch_size]\n",
    "        X = [_data(j * num_steps) for j in batch_indices]\n",
    "        Y = [_data(j * num_steps + 1) for j in batch_indices]\n",
    "        yield torch.tensor(X, dtype=torch.float32, device=device), torch.tensor(Y, dtype=torch.float32, device=device)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]\n"
    }
   ],
   "source": [
    "my_seq = list(range(30))\n",
    "print(my_seq)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "X:  tensor([[ 9., 10., 11.],\n        [ 6.,  7.,  8.],\n        [ 0.,  1.,  2.]], device='cuda:0') \nY: tensor([[10., 11., 12.],\n        [ 7.,  8.,  9.],\n        [ 1.,  2.,  3.]], device='cuda:0') \n\nX:  tensor([[15., 16., 17.],\n        [24., 25., 26.],\n        [18., 19., 20.]], device='cuda:0') \nY: tensor([[16., 17., 18.],\n        [25., 26., 27.],\n        [19., 20., 21.]], device='cuda:0') \n\nX:  tensor([[21., 22., 23.],\n        [12., 13., 14.],\n        [ 3.,  4.,  5.]], device='cuda:0') \nY: tensor([[22., 23., 24.],\n        [13., 14., 15.],\n        [ 4.,  5.,  6.]], device='cuda:0') \n\n"
    }
   ],
   "source": [
    "\n",
    "for X, Y in data_iter_random(my_seq, batch_size=3, num_steps=3):\n",
    "    print('X: ', X, '\\nY:', Y, '\\n')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.3.3.2 相邻采样\n",
    "\n",
    "令相邻的两个随机小批量在原始序列上的位置相毗邻。这时候，我们就可以用一个小批量最终时间步的隐藏状态来初始化下一个小批量的隐藏状态，从而使下一个小批量的输出也取决于当前小批量的输入，并如此循环下去"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 本函数已保存在d2lzh_pytorch包中方便以后使用\n",
    "# batch_size指每个小批量的样本数，\n",
    "# num_steps为每个样本所包含的时间步数\n",
    "def data_iter_consecutive(corpus_indices, batch_size, num_steps, device=None):\n",
    "    if device is None:\n",
    "        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "    corpus_indices = torch.tensor(corpus_indices, dtype=torch.float32, device=device)\n",
    "    data_len = len(corpus_indices)\n",
    "    batch_len = data_len // batch_size\n",
    "    indices = corpus_indices[0: batch_size*batch_len].view(batch_size, batch_len)\n",
    "    epoch_size = (batch_len - 1) // num_steps\n",
    "    for i in range(epoch_size):\n",
    "        i = i * num_steps\n",
    "        X = indices[:, i: i + num_steps]\n",
    "        Y = indices[:, i + 1: i + num_steps + 1]\n",
    "        yield X, Y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "X:  tensor([[ 0.,  1.,  2.],\n        [10., 11., 12.],\n        [20., 21., 22.]], device='cuda:0') \nY: tensor([[ 1.,  2.,  3.],\n        [11., 12., 13.],\n        [21., 22., 23.]], device='cuda:0') \n\nX:  tensor([[ 3.,  4.,  5.],\n        [13., 14., 15.],\n        [23., 24., 25.]], device='cuda:0') \nY: tensor([[ 4.,  5.,  6.],\n        [14., 15., 16.],\n        [24., 25., 26.]], device='cuda:0') \n\nX:  tensor([[ 6.,  7.,  8.],\n        [16., 17., 18.],\n        [26., 27., 28.]], device='cuda:0') \nY: tensor([[ 7.,  8.,  9.],\n        [17., 18., 19.],\n        [27., 28., 29.]], device='cuda:0') \n\n"
    }
   ],
   "source": [
    "for X, Y in data_iter_consecutive(my_seq, batch_size=3, num_steps=3):\n",
    "    print('X: ', X, '\\nY:', Y, '\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.7.7 64-bit ('d2l': conda)",
   "language": "python",
   "name": "python37764bitd2lconda94fc7ab78ae34cabbef0e75f5636f253"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7-final"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}